/* +----------------------------------------------------------------------
| 麦沃德科技赋能开发者，助力中小企业发展 
+----------------------------------------------------------------------
| Copyright (c) 2017～2024  www.wdadmin.cn    All rights reserved.
+----------------------------------------------------------------------
| 沃德代驾系统并不是自由软件，不加密，并不代表开源，未经许可不可自由转售和商用
+----------------------------------------------------------------------
| Author: MY WORLD Team <bd@maiwd.cn>   www.wdadmin.cn
+---------------------------------------------------------------------- */

import store from '@/store';

// 常量定义
const TOKEN_STORAGE_KEY = 'token'; // 本地存储的token键名

/**
 * 自定义HTTP错误类
 */
class HttpError extends Error {
	/**
	 * @param {string} message 错误信息
	 * @param {number} status 状态码
	 * @param {string} code 错误代码
	 */
	constructor(message, status, code) {
		super(message);
		this.name = 'HttpError';
		this.status = status || 0;
		this.code = code || `HTTP_${status}`;
		this.isAborted = code === 'REQUEST_ABORTED';
	}
}

/**
 * HTTP配置管理类
 */
class HttpConfig {
	/**
	 * @param {Object} options 配置选项
	 * @param {string} options.baseURL 基础URL
	 * @param {number} options.timeout 超时时间(ms)
	 * @param {Object} options.headers 默认请求头
	 */
	constructor(options = {}) {
		this.baseURL = options.baseURL || '';
		this.timeout = options.timeout || 30000;
		this.headers = {
			'Content-Type': 'application/x-www-form-urlencoded',
			...options.headers
		};
	}
}

/**
 * 拦截器管理类
 */
class InterceptorManager {
	constructor() {
		this.interceptors = [];
	}

	/**
	 * 添加拦截器
	 * @param {Function} fulfilled 成功处理函数
	 * @param {Function} rejected 失败处理函数
	 */
	use(fulfilled, rejected) {
		this.interceptors.push({ fulfilled, rejected });
	}
}

/**
 * 核心HTTP客户端类
 */
class HttpClient {
	constructor(options = {}) {
		// 初始化配置
		this.config = new HttpConfig(options);

		// 拦截器系统
		this.interceptors = {
			request: new InterceptorManager(),
			response: new InterceptorManager()
		};
	}

	// ------------------------ 核心请求方法 ------------------------

	/**
	 * 执行HTTP请求
	 * @param {string} apiConfig 接口路径数据
	 * @param {Object} params 请求参数
	 * @param {Object} customConfig 自定义配置
	 * @returns {Promise} 请求Promise
	 */
	async request(apiConfig, params = {}, customConfig = {}) {
		if (customConfig.baseURL) {
			this.config.baseURL = customConfig.baseURL
		} else {
			this.config.baseURL = getApp()?.globalData?.adminPath
		}
		try {
			// 前置网络检查
			if (!(await this._checkNetwork())) {
				throw new HttpError('网络连接不可用，请检查后重试', 503, 'NETWORK_UNAVAILABLE');
			}

			// 合并配置项
			const mergedConfig = this._mergeConfigs(apiConfig, params, customConfig);

			// 执行请求拦截链
			let chain = Promise.resolve(mergedConfig);
			this.interceptors.request.interceptors.forEach(({ fulfilled, rejected }) => {
				chain = chain.then(fulfilled, rejected);
			});

			// 发送请求
			try {
				const transformedConfig = await chain;
				const response = await this._dispatchRequest(transformedConfig);

				// 执行响应拦截链
				let responseChain = Promise.resolve(response);
				this.interceptors.response.interceptors.forEach(({ fulfilled, rejected }) => {
					responseChain = responseChain.then(fulfilled, rejected);
				});

				return await responseChain;
			} catch (error) {
				// 执行响应错误拦截链
				let errorChain = Promise.reject(error);
				this.interceptors.response.interceptors.forEach(({ fulfilled, rejected }) => {
					if (rejected) {
						errorChain = errorChain.catch(rejected);
					}
				});

				return await errorChain;
			}
		} catch (error) {
			return this._handleError(error);
		}
	}

	// ------------------------ 功能方法 ------------------------

	/**
	 * 发送实际请求
	 * @param {Object} config 请求配置
	 */
	async _dispatchRequest(config) {
		return new Promise((resolve, reject) => {
			uni.request({
				...config,
				url: this.config.baseURL + config.url,
				success: (res) => {
					try {
						this._validateStatusCode(res.statusCode);
						resolve(res.data);
					} catch (error) {
						reject(error);
					}
				},
				fail: (err) => {
					reject(this._createRequestError(err));
				}
			});
		});
	}

	// ------------------------ 工具方法 ------------------------

	/** 网络状态检测 */
	async _checkNetwork() {
		try {
			const { networkType } = await uni.getNetworkType();
			return networkType !== 'none';
		} catch (e) {
			return false;
		}
	}

	// 路径参数解析方法
	_resolvePathParams(url, params) {
		return url.replace(/\{(\w+)\}/g, (_, key) => params[key] ?? '');
	}

	/** 合并配置项 */
	_mergeConfigs(apiConfig, params, customConfig) {
		if (apiConfig.auth && !uni.getStorageSync(TOKEN_STORAGE_KEY)) {
			this._handleAuthError();
			throw new HttpError('未授权访问', 401);
		}
		const pathParams = { ...params };
		const queryParams = { ...params };
		const urlParams = (apiConfig.url.match(/\{(\w+)\}/g) || []).map(m => m.slice(1, -1));
		urlParams.forEach(k => delete queryParams[k]);
		return {
			url: this._resolvePathParams(apiConfig.url, pathParams),
			data: queryParams,
			...this.config,
			...customConfig,
			method: apiConfig.method,
			header: {
				...this.config.headers,
				...(uni.getStorageSync(TOKEN_STORAGE_KEY) ? {
					token: uni.getStorageSync(TOKEN_STORAGE_KEY)
				} : {}),
				// #ifdef MP-WEIXIN
				channel: 1,
				// #endif
				// #ifndef MP-WEIXIN
				channel: 2,
				// #endif
				...customConfig.headers
			}
		};
	}

	/** 错误处理 */
	_handleError(error) {
		// 统一错误提示
		const showToast = (msg) => uni.showToast({ title: msg, icon: 'none' });

		if (error instanceof HttpError) {
			switch (error.status) {
				case 401:
					this._handleAuthError();
					break;
				case 408:
					// showToast('请求超时，请稍后重试');
					break;
				case 500:
					showToast('服务器开小差了，请稍后再试');
					break;
				default:
					showToast(error.message);
			}
		} else {
			showToast('未知错误，请联系管理员');
		}

		// 开发环境日志
		if (process.env.NODE_ENV === 'development') {
			console.error('[HTTP Error]', error);
		}

		return Promise.reject(error);
	}

	/** 认证失败处理 */
	_handleAuthError() {
		uni.hideLoading();
		store.commit('user/clearAuth');
		this._redirectToLogin();
	}

	/** 跳转登录页 */
	_redirectToLogin() {
		const pages = getCurrentPages()
		uni.redirectTo({
			url: "/pages/public/login/index?from=" + encodeURIComponent(pages[pages.length - 1].$page.fullPath),
			animationType: "fade-in"
		})
	}

	/** 创建状态码错误对象 */
	_createStatusCodeError(statusCode) {
		const errorMap = {
			400: new HttpError('请求参数错误', 400),
			401: new HttpError('身份验证失败', 401),
			403: new HttpError('没有访问权限', 403),
			404: new HttpError('请求资源不存在', 404),
			408: new HttpError('请求超时', 408),
			429: new HttpError('请求过于频繁', 429),
			500: new HttpError('服务器内部错误', 500),
			502: new HttpError('网关错误', 502),
			503: new HttpError('服务不可用', 503),
			504: new HttpError('网关超时', 504)
		};
		return errorMap[statusCode] || new HttpError(`请求失败: ${statusCode}`, statusCode);
	}

	/**
	 * 验证HTTP状态码
	 * @param {number} statusCode 状态码
	 */
	_validateStatusCode(statusCode) {
		// 2xx 状态码直接通过
		if (statusCode >= 200 && statusCode < 300) return;

		// 创建对应错误对象
		const error = this._createStatusCodeError(statusCode);

		// 特殊状态码处理
		switch (statusCode) {
			case 401:
				this._handleAuthError();
				break;
			case 429:
				console.warn('接口请求过于频繁');
				break;
			default:
				if (getCurrentPages()[getCurrentPages().length - 1].route != "pages/public/error/error") {
					uni.redirectTo({
						url: "/pages/public/error/error"
					})
				}
		}

		throw error;
	}

	/** 创建请求错误对象 */
	_createRequestError(err) {
		return err.errMsg.includes('timeout') ?
			new HttpError('请求超时', 408) :
			new HttpError('网络连接失败', 0);
	}
}

// ------------------------ 实例初始化 ------------------------

// 创建全局实例
const httpClient = new HttpClient({
	baseURL: '',
});

// 添加默认请求拦截器（显示加载状态）
httpClient.interceptors.request.use(config => {
	try {
		if (config.loading) {
			uni.showLoading({
				title: config.loadingText || '加载中...',
				mask: true
			});
		}
		return config;
	} catch (error) {
		return Promise.reject(error);
	}
});

// 添加默认响应拦截器
httpClient.interceptors.response.use(
	response => {
		return response;
	},
	error => {
		uni.hideLoading();
		return Promise.reject(error);
	}
);

export default httpClient;